package pet_pavilion.yushougeums.service.impl;

import cn.tedu.yushouge.commons.ex.ServiceException;
import cn.tedu.yushouge.commons.pojo.po.AdminLoginInfoPO;
import cn.tedu.yushouge.commons.pojo.po.UserLoginInfoPO;
import cn.tedu.yushouge.commons.security.LoginPrincipal;
import cn.tedu.yushouge.commons.web.JsonResult;
import cn.tedu.yushouge.commons.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Range;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import pet_pavilion.yushougeums.mapper.UserMapper;
import pet_pavilion.yushougeums.pojo.dto.UserLoginInfoDTO;
import pet_pavilion.yushougeums.pojo.entity.User;
import pet_pavilion.yushougeums.pojo.param.UserAddNewParam;
import pet_pavilion.yushougeums.pojo.param.UserLoginInfoParam;
import pet_pavilion.yushougeums.pojo.param.UserUpdateInfoParam;
import pet_pavilion.yushougeums.pojo.vo.UserStandardVO;
import pet_pavilion.yushougeums.repository.IUserJwtRepository;
import pet_pavilion.yushougeums.security.UserDetails;
import pet_pavilion.yushougeums.service.IUserService;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    @Value("${yushouge.crud.default-query-page-size}")
    private Integer defaultQueryPageSize;

    @Value("${yushouge.jwt.secret-key}")
    private String secretKey;

    @Value("${yushouge.jwt.duration-in-minute}")
    private Long durationInMinute;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private IUserJwtRepository userCacheRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;



    @Override
    public UserLoginInfoDTO login(UserLoginInfoParam adminLoginInfoParam, String remoteAddr, String userAgent) {
        log.debug("开始处理【用户登录】的业务，参数：{}", adminLoginInfoParam);
        org.springframework.security.core.Authentication authentication = new UsernamePasswordAuthenticationToken(
                adminLoginInfoParam.getUsername(), adminLoginInfoParam.getPassword());
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("认证通过！（如果未通过，过程中将抛出异常，你不会看到此条日志！）");
        log.debug("认证结果：{}", authenticateResult);
        log.debug("认证结果中的当事人：{}", authenticateResult.getPrincipal());
        UserDetails userDetails = (UserDetails) authenticateResult.getPrincipal();

        // 使用JWT机制时，登录成功后不再需要将认证信息存入到SecurityContext
        // SecurityContext securityContext = SecurityContextHolder.getContext();
        // securityContext.setAuthentication(authenticateResult);

        // 需要存入到JWT中的数据
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", userDetails.getId());
        claims.put("username", userDetails.getUsername());
        String authorityListJsonString = JSON.toJSONString(userDetails.getAuthorities());
        // claims.put("authoritiesJsonString", authorityListJsonString);

        // 生成JWT，以下代码是相对固定的
        long tokenDuration = durationInMinute * 60 * 1000;
        Date tokenExpiration = new Date(System.currentTimeMillis() + tokenDuration);
        String jwt = Jwts.builder()
                // Header：声明算法与Token类型
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload：数据，具体表现为Claims
                .setClaims(claims)
                .setExpiration(tokenExpiration)
                // Verify Signature：验证签名
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("生成了JWT数据：{}", jwt);

        // 需要缓存到Redis中的数据
        UserLoginInfoPO userLoginInfoPO = new UserLoginInfoPO();
        userLoginInfoPO.setId(userDetails.getId());
        userLoginInfoPO.setEnable(userDetails.isEnabled() ? 1 : 0);
        userLoginInfoPO.setRemoteAddr(remoteAddr);
        userLoginInfoPO.setUserAgent(userAgent);
        userLoginInfoPO.setAuthorityListJsonString(authorityListJsonString);
        userCacheRepository.saveLoginInfo(jwt, userLoginInfoPO, tokenDuration);
        log.debug("向Redis中写入此用户的登录信息：{}", userLoginInfoPO);



        // 准备返回
        UserLoginInfoDTO userLoginInfoDTO = new UserLoginInfoDTO()
                .setId(userDetails.getId())
                .setUsername(userDetails.getUsername())
                .setToken(jwt)
                .setTokenExpiration(tokenExpiration);
        return userLoginInfoDTO;
    }


    @Override
    public void logout(String jwt) {
        log.debug("开始处理【用户退出登录】的请求，参数：{}", jwt);
        userCacheRepository.delete(jwt);
    }
    @Override
    public void addNew(@Valid UserAddNewParam userAddNewParam) {

        log.debug("开始处理【添加用户】的业务，参数：{}", userAddNewParam);
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("username",userAddNewParam.getUsername());

        int countByUsername = userMapper.selectCount(queryWrapper);

        log.debug("根据用户名统计匹配的用户数量，结果：{}",countByUsername);
        if (countByUsername > 0){
            String message ="添加用户失败，用户名已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }

        // TODO 检查用户手机号码是否被占用，如果被占用，则抛出异常
        QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("telephone", userAddNewParam.getTelephone());
        int countByPhone = userMapper.selectCount(queryWrapper2);
        log.debug("根据用户手机号码统计匹配的用户数量，结果：{}", countByPhone);
        if (countByPhone > 0) {
            String message = "添加用户失败，手机号码已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        //将用户添加到数据库中
        User user = new User();
        BeanUtils.copyProperties(userAddNewParam, user);
        user.setPermissionId(1L);
        String rawPassword = user.getPassword();
        String encodedPassword = passwordEncoder.encode(rawPassword);
        user.setPassword(encodedPassword);
        int rows = userMapper.insert(user);
        if (rows != 1){
            String message = "添加用户失败，服务器忙，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }
        log.info("将新的用户数据插入到数据库，完成！");
    }

    @Override
    public void updateById(Long id, UserUpdateInfoParam userUpdateInfoParam) {
        log.debug("开始处理【修改用户信息详情】的业务，ID：{}，新数据：{}", id, userUpdateInfoParam);
        // 检查相册是否存在，如果不存在，则抛出异常
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        int countById = userMapper.selectCount(queryWrapper);
        log.debug("根据用户ID统计匹配的用户数量，结果：{}", countById);
        if (countById == 0) {
            String message = "修改用户密码失败，用户不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("telephone", userUpdateInfoParam.getTelephone())
                .ne("id", id);
        int countByPhone = userMapper.selectCount(queryWrapper2);
        log.debug("根据用户手机号码统计匹配的用户数量，结果：{}", countByPhone);
        if (countByPhone > 0) {
            String message = "修改用户信息详情失败，用户手机号码已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        QueryWrapper<User> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("email", userUpdateInfoParam.getEmail())
                .ne("id", id);
        int countByEmail = userMapper.selectCount(queryWrapper3);
        log.debug("根据用户邮箱统计匹配的用户数量，结果：{}", countByEmail);
        if (countByEmail > 0) {
            String message = "修改用户信息详情失败，用户邮箱已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        User user = new User();
        user.setId(id);
        String newPassword = userUpdateInfoParam.getPassword();
        user.setPassword(passwordEncoder.encode(newPassword));
        BeanUtils.copyProperties(userUpdateInfoParam, user);
        int rows = userMapper.updateById(user);
        if (rows != 1) {
            String message = "修改用户信息失败，服务器忙，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
        log.debug("将新的用户信息更新入到数据库，完成！");
    }

    @Override
    public UserStandardVO getStandardById(Long id) {
        log.debug("开始处理【根据ID查询用户详情】的业务，参数：{}", id);
        UserStandardVO queryResult = userMapper.getStandardById(id);
        if (queryResult == null) {
            String message = "查询用户详情失败，用户数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        return queryResult;
    }

    @Override
    public void setEnable(Long id) {
        log.debug("开始处理【启用用户】的业务，参数：{}", id);
        updateEnableById(id, 1);
    }

    private void updateEnableById(Long id, Integer enable) {
        UserStandardVO currentAdmin = userMapper.getStandardById(id);

        if (currentAdmin == null) {
            String message = ENABLE_TEXT[enable] + "用户失败，用户数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        if (currentAdmin.getEnable() == enable) {
            String message = ENABLE_TEXT[enable] + "用户失败，此用户已经处于" + ENABLE_TEXT[enable] + "状态！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        User updateUser = new User();
        updateUser.setId(id);
        updateUser.setEnable(enable);

        int rows = userMapper.updateById(updateUser);
        if (rows != 1) {
            String message = ENABLE_TEXT[enable] + "用户失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
        }
}
